/* imported for its side-effect of adding HTTP handlers:
   GET  /
   GET  /{graph}.html
   GET  /{graph}/{node}.json
   GET  /{graph}/{node}/{relation}.json
   GET  /{graph}/{node}.html
   POST /{graph}/{node}/{relation}/{node2}
   POST /{graph}/{node}/{relation}
   PUT  /{graph}/{node}
*/
package httpd

import (
	"code.google.com/p/feedgraph/graph"
	"code.google.com/p/feedgraph/rest"
	"code.google.com/p/gorilla/mux"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"
	"encoding/json"
	"strconv"
)

// max number of entries to return at one time
const MAX_LOAD = 100

var graphs map[string]*graph.Graph = make(map[string]*graph.Graph)


func AddGraph(id string, capacity int) {

	graphs[id] = graph.New(id, capacity)
}

func Graph(id string) *graph.Graph {

	return graphs[id]
}

func graphHTMLHandler(w http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)

	gid := vars["graph"]

	graph, ok := graphs[gid]

	if !ok {
		log.Println("client requested graph ", gid)
		w.WriteHeader(404)
		return
	}

	fmt.Fprintf(w, "<html><body>%q<ul>", gid)

	for _, row := range graph.Rows {

		if row.Id == "" {
			continue
		}

		//urlstr := "/" + gid + "/" + row.Id + ".html"
		relstr := "/" + gid + "/" + row.Id + "/" + row.Rel + ".json"
		contenturl := "/js/editor.html?/" + gid + "/" + row.Id + ".txt"

		fmt.Fprintf(w, "<li>%q", row.Id)
		fmt.Fprintf(w, " -&gt; (<a href=%q>%q</a>) : %q", relstr, row.Rel, row.Edge.Object)
		fmt.Fprintf(w, " <a href=%q>[edit]</a></li>", contenturl)
	}

	fmt.Fprintf(w, "</ul></body></html>")
}

func nodeListRelationsHandler(w http.ResponseWriter, req *http.Request) {

	optionsHandler(w, req)

	if req.Method != "GET" {

		log.Println("expected GET request, got ", req.Method)
		return
	}	

	vars := mux.Vars(req)

	gid := vars["graph"]
	nid := vars["node"]

	graph, ok := graphs[gid]

	if !ok {
		w.WriteHeader(404)
		return
	}


	results := graph.Relations(nid)

	rest.WriteObject(w, req, results)
}

func nodeRelationHandler(w http.ResponseWriter, req *http.Request) {

	optionsHandler(w, req)

	if req.Method != "GET" {

		log.Println("expected GET request, got ", req.Method)
		return
	}	

	vars := mux.Vars(req)

	gid := vars["graph"]
	nid := vars["node"]
	rel := vars["relation"]

	sincestr := req.FormValue("since")
	minstr := req.FormValue("min")
	maxstr := req.FormValue("max")
	since := 0
	min := 1
	max := MAX_LOAD

	if minstr != "" {
		m, err := strconv.Atoi(minstr)

		if err != nil {
			log.Println(err)
			w.WriteHeader(400)
			return
		}

		min = m
	}

	if maxstr != "" {
		m, err := strconv.Atoi(maxstr)

		if err != nil {
			log.Println(err)
			w.WriteHeader(400)
			return
		}

		max = m
	}

	if sincestr != "" {
		m, err := strconv.Atoi(sincestr)

		if err != nil {
			log.Println(err)
			w.WriteHeader(400)
			return
		}

		since = m
	}

	graph, ok := graphs[gid]

	if !ok {
		w.WriteHeader(404)
		return
	}

	blocking := req.FormValue("block") == "true"

	results := graph.Edges(nid, rel, since)

	client := req.Header.Get("X-Forwarded-For")
	
	if client == "" {
		client = req.RemoteAddr
	}

	node := graph.Node(nid)

	if len(results) < min && blocking {

		node.Cond.L.Lock()

		for len(results) < min {
			time.Sleep(500 * time.Millisecond)
			node.Wait()
			results = graph.Edges(nid, rel, since)
		}

		node.Cond.L.Unlock()
	}

	if len(results) > max {
		results = results[0:max]
	}

	rest.WriteObject(w, req, results)
}


/*
func nodeHTMLHandler(w http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)

	gid := vars["graph"]
	nid := vars["node"]

	graph, ok := graphs[gid]

	if !ok {
		w.WriteHeader(404)
		return
	}

	node := graph.Node(nid)

	fmt.Fprintf(w, "<html><body>%q / %q<ul>", gid, nid)

	lines := 0

	for rel, edges := range node.Edges() {

		fmt.Fprintf(w, "<li>%q<ul>", rel)
		q := edges.Since(-1)
		for _, v := range q {

			urlstr := "/" + url.QueryEscape(gid) + "/" + url.QueryEscape(v.Id) + ".html"
			fmt.Fprintf(w, "<li>[%d] <a href=%q>%q</a> %s</li>", v.Index, urlstr, v.Id, v.Time)

			lines++

			if lines > 200 {

				fmt.Fprintf(w, "</ul>[TRUNCATED]</body></html>")
				return
			}
		}
		fmt.Fprintf(w, "</ul>")
	}

	fmt.Fprintf(w, "</ul></body></html>")

}

func nodeGVHandler(w http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)

	gid := vars["graph"]
	nid := vars["node"]

	graph, ok := graphs[gid]

	if !ok {
		w.WriteHeader(404)
		return
	}

	node := graph.Node(nid)

	fmt.Fprintf(w, "strict digraph %q {", nid)

	if len(nid) > 15 {
		fmt.Fprintf(w, "%q [label=%q]\n", nid, nid[:15])
	}

	lines := 0

	for rel, edges := range node.Edges() {

		q := edges.Since(-1)
		for _, v := range q {

			//urlstr := "/" + gid + "/" + v.Id + ".gv"

			if len(rel) > 15 {
				fmt.Fprintf(w, "%q -> %q [label=%q fontsize=\"10\"]\n", nid, v.Id, rel[:15])
			} else {
				fmt.Fprintf(w, "%q -> %q [label=%q fontsize=\"10\"]\n", nid, v.Id, rel)
			}

			lines++

			if lines > 200 {

				return
			}
		}
	}

	fmt.Fprintf(w, "}")

}
*/

func contentHandler(w http.ResponseWriter, req *http.Request) {

	optionsHandler(w, req)

	if req.Method != "GET" {
		return
	}

	vars := mux.Vars(req)
	gid := vars["graph"]
	nid := vars["node"]

	g, ok := graphs[gid]

	if !ok {
		w.WriteHeader(404)
		return
	}

	content, err := json.Marshal(g.Node(nid).Object)

	if err != nil {
		w.WriteHeader(400)
		return
	}

	w.Write(content)
}

func updateHandler(w http.ResponseWriter, req *http.Request) {

	optionsHandler(w, req)

	if req.Method != "POST" && req.Method != "PUT" {
		return
	}

	vars := mux.Vars(req)
	gid := vars["graph"]
	nid := vars["node"]

	g, ok := graphs[gid]

	if !ok {
		w.WriteHeader(404)
		return
	}

	body, err := ioutil.ReadAll(req.Body)

	if err != nil {
		w.WriteHeader(400)
		return
	}

	defer req.Body.Close()

	err = json.Unmarshal(body, &g.Node(nid).Object)

	if err != nil {
		w.WriteHeader(400)
		return
	}
}

func linkObjHandler(w http.ResponseWriter, req *http.Request) {

	optionsHandler(w, req)

	if req.Method != "POST" && req.Method != "PUT" {
		return
	}

	vars := mux.Vars(req)
	gid := vars["graph"]
	nid := vars["node"]
	r := vars["relation"]
	uuid := req.FormValue("uuid")

	g, ok := graphs[gid]

	if !ok {
		w.WriteHeader(404)
		return
	}

	text, err := ioutil.ReadAll(req.Body)

	if err != nil {
		log.Println(err)
		return
	}

	var obj map[string]interface{}

	err = json.Unmarshal(text, &obj)

	log.Println(req.Method, req.URL.Path, string(text), obj)

	if err != nil {
		w.WriteHeader(400)
		return
	}

	g.Link(nid, r, obj, uuid)

}

/*
func listHTMLHandler(w http.ResponseWriter, req *http.Request) {

	fmt.Fprintf(w, "<html><body><ul>")

	for k := range graphs {

		fmt.Fprintf(w, "<li><a href=\"/%s.html\">%s</a></li>", url.QueryEscape(k), k)
	}

	fmt.Fprintf(w, "</ul></body></html>")
}
*/

func listHandler(w http.ResponseWriter, req *http.Request) {

	var res []string

	for k := range graphs {
		res = append(res, k)
	}

	rest.WriteObject(w, req, res)
}

func listNodesHandler(w http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)

	gid := vars["graph"]

	graph, ok := graphs[gid]

	if !ok {
		log.Println("client requested graph ", gid)
		w.WriteHeader(404)
		return
	}

	var res []string
	
	for _, row := range graph.Rows {

		if row.Id == "" {
			continue
		}

		res = append(res, row.Id)
	}


	rest.WriteObject(w, req, res)
}

func deleteEdgeHandler(w http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)
	id := req.FormValue("id");

	if id == "" {
		w.WriteHeader(400)
		return
	}

	gid := vars["graph"]

	graph, ok := graphs[gid]

	if !ok {
		log.Println("client requested graph ", gid)
		w.WriteHeader(404)
		return
	}

	graph.Delete(id)

}

func optionsHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Methods", "PUT")
	w.Header().Add("Access-Control-Allow-Methods", "POST")
	w.Header().Add("Access-Control-Allow-Methods", "HEAD")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
}

func init() {

	r := mux.NewRouter()

	get := r.Methods("GET", "OPTIONS").Subrouter()
	put := r.Methods("PUT", "OPTIONS").Subrouter()
	post := r.Methods("PUT", "POST", "OPTIONS").Subrouter()
	delete := r.Methods("DELETE", "OPTIONS").Subrouter()

	get.HandleFunc("/", listHandler)
	get.HandleFunc("/{graph}", listNodesHandler)
//	get.HandleFunc("/index.html", listHTMLHandler)
//	get.HandleFunc("/index.json", listHandler)
//	get.HandleFunc("/{graph}.html", graphHTMLHandler)
	get.HandleFunc("/{graph}/{node}", contentHandler)
	get.HandleFunc("/{graph}/{node}/{relation}", nodeRelationHandler)
	get.HandleFunc("/{graph}/{node}/__RELATIONS", nodeListRelationsHandler)
//	get.HandleFunc("/{graph}/{node}.html", nodeHTMLHandler)
//	get.HandleFunc("/{graph}/{node}.gv", nodeGVHandler)

//	post.HandleFunc("/{graph}/{node}/{relation}/{node2}", linkHandler)
	post.HandleFunc("/{graph}/{node}/{relation}", linkObjHandler)
	put.HandleFunc("/{graph}/{node}", updateHandler)

	delete.HandleFunc("/{graph}", deleteEdgeHandler)

	http.Handle("/", r)
}
